package com.zhou.common.operator.log.core.service.impl;

import com.zhou.common.operator.log.core.OperateLogProperty;
import com.zhou.common.operator.log.core.model.OperateLogInfo;
import com.zhou.common.operator.log.core.service.OperateLogStorageService;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

@Slf4j
public abstract class AbstractStorageService implements OperateLogStorageService {
    private Integer defaultLength = 50;
    private Integer defaultInterval = 5;
    private OperateLogProperty properties;
    private LinkedBlockingQueue queue;
    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("operateLogPersistenceThread");
            return thread;
        }
    });
    private Runnable flushCommand = new Runnable() {
        @Override
        public void run() {
            AbstractStorageService.this.flushQueue();
        }
    };

    @PostConstruct
    private void init() {
        this.queue = new LinkedBlockingQueue(this.getQueueLength());
        if (this.properties != null && this.properties.isSaveAsync()) {
            this.scheduledExecutorService.scheduleAtFixedRate(this.flushCommand, 0L, (long) this.getFlushInterval(), TimeUnit.SECONDS);
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                this.scheduledExecutorService.shutdownNow();
                this.flushCommand.run();
            }));
        }

    }

    @Override
    public void append(OperateLogInfo newItem) {
        if (this.properties != null && this.properties.isSaveAsync()) {
            if (newItem == null) {
                return;
            }

            boolean result = this.queue.offer(newItem);
            if (result) {
                return;
            }

            this.flushQueue();
            this.queue.offer(newItem);
        } else {
            this.saveBatch(Arrays.asList(newItem));
        }

    }

    private void flushQueue() {
        List<OperateLogInfo> items = new ArrayList();
        this.queue.drainTo(items);

        try {
            if (!items.isEmpty()) {
                this.saveBatch(items);
            }
        } catch (Exception e) {
            log.error("保存操作日志失败", e);
//            this.queue.addAll(items);
        }

    }

    protected abstract void saveBatch(List<OperateLogInfo> logs);

    protected Integer getQueueLength() {
        Integer queueLength = this.defaultLength;
        if (this.properties != null && this.properties.getQueueLength() != null) {
            queueLength = this.properties.getQueueLength();
        }

        return queueLength;
    }

    protected Integer getFlushInterval() {
        Integer interval = this.defaultInterval;
        if (this.properties != null && this.properties.getQueueLength() != null) {
            interval = this.properties.getFlushPerSeconds();
        }

        return interval;
    }

    public OperateLogProperty getProperties() {
        return this.properties;
    }

    public AbstractStorageService(OperateLogProperty property) {
        this.properties = property;
    }
}

